package org.oep.jmingle;

import java.util.Hashtable;
import java.util.Vector;

import org.oep.widgets.MonologueWidget;

public class Scripting {
	public static final int
		TYPE_INVALID = 0,
		TYPE_DISPLAY = TYPE_INVALID + 1,
		TYPE_WAIT = TYPE_INVALID + 2,
		TYPE_MINGLET = TYPE_INVALID + 3,
		TYPE_CREATE = TYPE_INVALID + 4,
		TYPE_CENTER = TYPE_INVALID + 5,
		TYPE_NOTHING = TYPE_INVALID + 1000;

	public static final String
		KEYWORD_DISPLAY = "DISPLAY",
		KEYWORD_MINGLET = "MINGLET",
		KEYWORD_CREATE = "CREATE",
		KEYWORD_CENTER = "CENTER",
		KEYWORD_WAIT = "WAIT";

	public static final Object BINDINGS[][] = {
		{KEYWORD_CENTER, new Integer(TYPE_CENTER)},
		{KEYWORD_MINGLET, new Integer(TYPE_MINGLET)},
		{KEYWORD_CREATE, new Integer(TYPE_CREATE)},
		{KEYWORD_DISPLAY, new Integer(TYPE_DISPLAY)},
		{KEYWORD_WAIT, new Integer(TYPE_WAIT)},
	};
	
	
	private Node head;
	
	private Node nextInstruction;
	
	private Field field;
	
	private Scripting(String filename) {
		parse(filename);
	}
	
	
	public void eval() {
		eval(nextInstruction.next);
	}
	
	public boolean isWaiting() {
		return nextInstruction != null && nextInstruction.type == TYPE_WAIT;
	}
	
	public void addMinglet(String key, Minglet value) {
		System.out.println("Add minglet");
		field.minglets.put(key,value);
		System.out.println("Add minglet out");
	}
	
	public Minglet getMinglet(String key) {
		return (Minglet) field.minglets.get(key);
	}
	
	private void eval(Node s) {
		if(s == null) return;
		nextInstruction = s;
		
		System.out.println("Evaluating line " + s.line);
		
		switch(s.type) {
		case TYPE_MINGLET: evalMinglet(s); break;
		case TYPE_DISPLAY: evalDisplay(s); break;
		case TYPE_WAIT: return;
		}
		
		eval(s.next);
	}
	
	private void evalMinglet(Node s) {
		switch(s.subtype) {
		case TYPE_CREATE: evalMingletCreate(s); break;
		case TYPE_CENTER: evalMingletCenter(s); break;
		default: error("Invalid subtype for minglet", s.subtype); return;
		}
	}
	
	private void evalMingletCreate(Node s) {
		String key;
		String mac = Integer.toHexString( JMingle.RANDOM.nextInt() );
		String owner = "Script-created";
		
		System.out.println("Switch!");
		switch(s.args.length) {
		case 4:
			owner = s.args[3];
		case 3:
			System.out.println("Mac time!!!");
			mac = performReplacements(s.args[2]);
			System.out.println("The mac is: " + mac);
		case 2:
			key = s.args[1];
			Minglet a = Utils.makeMinglet(mac, owner);
			a.setAnimationState(Minglet.KEY_STAND);
			addMinglet(key, a);
			break;
			
		default:
			error("Invalid number of arguments to MINGLET CREATE", s.args.length);	
			break;
		}
	}
	
	private void evalMingletCenter(Node s) {
		switch(s.args.length) {
		case 2:
			String who = s.args[1];
			Minglet m = getMinglet(who);
			m.setPosition(field.getWidth() / 2, (field.BOUNDARY_BOTTOM + field.BOUNDARY_TOP) / 2);
			break;
			
		default:
			error("Invalid number of arguments to MINGLET CENTER", s.args.length);	
			break;
		}
	}
	
	private void evalDisplay(Node s) {
		String msg = Utils.join(s.args, ' ');
		msg = performReplacements(msg);
		field.tutorialBox.display(msg);
		field.tutorialBox.setActive(true);
	}
	
	private void parse(String filename) {
		Vector lines = Utils.getLines(filename);
		Node prev = new Node(TYPE_INVALID, TYPE_INVALID, null);
		nextInstruction = prev;
		Node current = null;
		for(int i = 0; i < lines.size(); i++) {
			String line = (String) lines.elementAt(i);
			String tokens[] = Utils.split(line, ' ');
			int type = chooseType(tokens[0]);
			int subtype = (tokens.length > 1) ? chooseType(tokens[1]) : TYPE_INVALID;
			
			String args[] = new String[tokens.length-1];
			
			// Transfer over the args from the tokens array.
			for(int j = 1; j < tokens.length; j++) { args[j-1] = tokens[j]; } 
			
			current = new Node(type, subtype, args);
			current.line = i+1;
			
			if(i == 0) {
				head = current;
			}
			
			current.prev = prev;
			prev.next = current;
			
			prev = current;
		}
		prev.next = new Node(TYPE_INVALID, TYPE_INVALID, null);
	}
	
	private String performReplacements(String msg) {
		String mac = field.bt.getMyMAC();
		String playerName = Utils.makeName(mac);
		
		System.out.println("Mac: " + mac);
		
		msg = Utils.replaceAllIgnoreCase(msg, "%name%", playerName);
		msg = Utils.replaceAllIgnoreCase(msg, "%mac%", field.bt.getMyMAC());
		
		return msg;
	}
	
	private int chooseType(String word) {
		for(int i = 0; i < BINDINGS.length; i++) {
			String str = (String) BINDINGS[i][0];
			int value = ((Integer) BINDINGS[i][1]).intValue();
			if(word.equalsIgnoreCase(str)) {
				return value;
			}
		}
		return TYPE_INVALID;
	}
	
	static class Node {
		int type;
		
		int subtype;
		
		int line;
		
		Node prev, next;
		
		String args[];
		
		Node(int t, int st, String a[]) {
			type = t;
			subtype = st;
			args = a;
		}
	}
	
	public static Scripting createEnvironment(Field field, String script) {
		System.out.println("Parsing the script!");
		Scripting s = new Scripting(script);
		s.field = field;
		System.out.println("I parsed the script!");
		return s;
	}
	
	private static void error(String msg, int code) {
		System.err.println(msg + " (Code: " + code + ")");
	}
}
